home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.cs.arizona.edu
/
ftp.cs.arizona.edu.tar
/
ftp.cs.arizona.edu
/
icon
/
newsgrp
/
group01b.txt
/
000091_icon-group-sender_Fri Jul 6 09:39:50 2001.msg
< prev
next >
Wrap
Internet Message Format
|
2002-01-03
|
5KB
Return-Path: <icon-group-sender>
Received: (from root@localhost)
by baskerville.CS.Arizona.EDU (8.11.1/8.11.1) id f66Gcbo19262
for icon-group-addresses; Fri, 6 Jul 2001 09:38:37 -0700 (MST)
Message-Id: <200107061638.f66Gcbo19262@baskerville.CS.Arizona.EDU>
Date: Fri, 6 Jul 2001 14:28:00 +1200 (NZST)
From: "Richard A. O'Keefe" <ok@atlas.otago.ac.nz>
To: art.eschenlauer@sufsys.com, icon-group@cs.arizona.edu
Subject: Re: Software testing for Icon?
Errors-To: icon-group-errors@cs.arizona.edu
Status: RO
Content-Length: 4722
One concern that I expect people to raise with respect to using
Icon in the "mainstream" is, "Icon cannot be trusted because it
does not typecheck arguments at compile time."
Neither does Perl. Doesn't seem to have slowed Perl down any.
Neither do TCL, Python, Smalltalk, Scheme.
For that matter, NEITHER DOES JAVA, practically speaking.
In effect, any time you stuff something in a collection object,
you lose all compile-time information about its type.
All of these languages *do* type-check arguments at run-time.
Oh yes, you can add Javascript and Visual Basic (some variables are
type-checked at compile time, some are not.)
If anyone tries that argument on you, ask them whether that stops
them using Java or Visual Basic.
There is another fairly major point, which is "what kind of type
system are we comparing against?" For example, can it express
the following constraint I actually used today in a design:
data Tree k v = Leaf | Node k v (Tree k v) (Tree k v)
size :: Tree k v -> Int
size (Leaf) = 0
size (Node _ _ l r) = size l + size r
well_ordered :: (Ord k) => Tree k v -> Bool
well_ordered (Leaf) = True
well_ordered (Node k _ l r) = well_ordered l && well_ordered r &&
all_less l && all_grtr r
where all_less (Leaf) = True
all_less (Node x _ l r) = x < k && all_less l && all_less r
all_grtr (Leaf) = True
all_grtr (Node x _ l r) = x > k && all_grtr l && all_grtr r
well_shaped :: Tree k v -> Bool
well_shaped (Node _ _ l r) = sl <= sr && sr <= sl + 1
where { sl = size l; sr = size r }
well_shaped (Leaf) = True
Now, here's the constraint I want:
"type GoodTree k v = Tree k v SUCH THAT well_ordered && well_shaped"
It so happens that the language (Haskell) that I've used above CAN'T
express that constraint. You cannot express "is a size balanced
binary search tree" in the type language.
This kind of thing is EXTREMELY common. Part of your requirements can be
expressed in the type system, but ONLY part. The fact that your program
has got through the compiler's type checking is *N*O* guarantee that you
will not get a *conceptual* type error at run time. It really is not a
contest between "no type checking" (Icon) and "strong type checking"
(C++, say), but subspaces of constraint space, where C++ (with ALL of its
type checking) falls in the "unbelievably weak" part of constraint space.
Your interlocutor might be tempted to say "well of COURSE the compiler
doesn't type check class invariants, that's what modules and encapsulation
are for", to which you reply "yes, that's why Icon has them."
"How can you protect against programmer errors in the arguments
passed during infrequently-executed invocations?"
The question is "do you need to?" What is the evidence that the risk
of such errors, for the kinds of applications you write, is high enough
to spend any of your programming budget on?
I don't think that the response (however true) that C++ has
compile-time type-checking and yet still is notorious for null
pointer errors, etc, will convince anybody.
Especially as "type-STATE checking" (null-vs-non-null, for example)
*could* be handled by well understood type-checking-like approaches.
As a simple example, suppose that there were two families of types:
T *x; possibly-null pointer to T
T ^y; non-null pointer to T
and that
x = y; was always allowed, but
y = x; would require an explicit run-time-checking cast
Then you could define
char ^strcpy(char ^dst, char const ^src) {...}
This raises two questions in my mind regarding Icon:
1. Should one adopt a "defensive programming style", always checking the
arguments passed to each routine?
That's an economic question. Does the observed frequency of such errors
in your code justify the development-time cost of writing such tests?
What is the performance penalty of such tests? Don't forget, the REAL
checks you need include checks for well-formedness of conceptual type,
not just the things you might have checked in C++.
The usual rule of thumb is "check on entry to module, not on module-internal
calls", so even if you do decide to have such checks, you would by no means
put them in every procedure.
2. What work has been done on developing rigorous software-testing
methodology for Icon programs?
The usual sort of path coverage stuff (which is very very hard to achieve
anyway) is blown out of the water by exceptions, which add invisible paths
all over the place. Icon backtracking does much the same thing. However,
the notion of statement coverage is still pretty useful.